#include <iostream>
#include "auto_array.hpp"

using namespace std;

int main(int argc, char *argv[])
{
    auto_array<int,1> a;
    a.reshape(0,5);
    try {
    a[0]=1;
    a[0]+=2;
    a[1]=3;
    a[4]=a[0]*a[1];
    }
    catch(runtime_error &err)
    {
      cout<<err.what()<<endl;
      system("PAUSE");
      return 0;
    }
    //auto_array<int,2> a;
    //a.reshape(0,4)(1,5);
    //a[3][0]=1;
    if(!a.empty())
      cout<<"a is not null"<<endl;
    cout<<a[0]<<endl
        <<a[1]<<endl
        <<a[2]<<endl;
    cout<<a.size()<<endl
        <<a.max_size()<<endl;
    a=0;
    if(!a)
      cout<<"a is null now"<<endl;
   
    system("PAUSE");
    return EXIT_SUCCESS;
}

// 下一步,设计一个和boost::multi_array类似的'生成器',以下是源码,版权所有:

#ifndef AUTO_ARRAY
#define AUTO_ARRAY

#include <stdexcept>

//test allocator
template<typename T>
class _test_allocator
{
public:
  static void* allocate(std::size_t n)
  {
    return malloc(n*sizeof(T));
  }

  static void deallocate(void* p, std::size_t /* __n */)
  {
    free(p);
  }

  static void* reallocate(void* __p, std::size_t /* old_sz */, std::size_t __new_sz)
  {
    return realloc(__p, __new_sz*sizeof(T));
  }
};

struct range_list
{
  long start;
  std::size_t width;
  std::size_t step;
};

template<typename T, std::size_t dim, typename Allocator = _test_allocator<T> >
class auto_array
{
public:
  typedef T value_type;
  typedef value_type* pointer;
  typedef const value_type* const_pointer;
  typedef value_type* iterator;
  typedef const value_type* const_iterator;
  typedef value_type& reference;
  typedef const value_type& const_reference;
  typedef size_t size_type;
  typedef ptrdiff_t difference_type;
 
  auto_array() : range_changed(false), data(0), length(0), max_sz(0)
  {
    for(std::size_t i=0;i<dim;++i)
      rl[i].width=0;
  }
 
  template<std::size_t _dim>
  class reshape_functor
  {
  public:
    reshape_functor(range_list *prl) : rl(prl) {}
    reshape_functor operator()(std::size_t dim_index, std::size_t width, long start = 0)
    {
      if(dim_index>=_dim)
        throw(std::runtime_error("array reshape error: dimensionality too big"));
      rl[dim_index].start=start;
      rl[dim_index].width=width;
      return *this;
    }
  private:
    range_list *rl;
  };
 
  reshape_functor<dim>
  reshape(std::size_t dim_index, std::size_t width, long start = 0)
  {
    range_changed=true;
    return reshape_functor<dim>(rl)(dim_index,width,start);
  }

  template<typename _T, std::size_t _dim>
  class index_functor
  {
  public:
    index_functor(_T *pdata, const range_list *prl) : data(pdata), rl(prl) {}
    index_functor<_T,_dim-1> operator[](long index)
    {
      if(index>=rl[0].start && index-rl[0].start<static_cast<long>(rl[0].width))
        return index_functor<_T,_dim-1>(data+(index-rl[0].start)*rl[0].step,rl+1);
      else
        throw(std::runtime_error("array access error: out of bound"));
    }
  private:
    _T *data;
    const range_list *rl;
  };
 
  template<typename _T>
  class index_functor<_T,0>
  {
  public:
    index_functor(_T *pdata, const range_list *prl) : data(pdata) {}
    operator _T(){ return data[0]; }
   
    template<typename U>
    _T& operator=(const U &other){ return data[0]=other; }
   
    template<typename U>
    _T& operator+=(const U &other){ return data[0]+=other; }
   
    template<typename U>
    _T& operator-=(const U &other){ return data[0]-=other; }
   
    template<typename U>
    _T& operator*=(const U &other){ return data[0]*=other; }
   
    template<typename U>
    _T& operator/=(const U &other){ return data[0]/=other; }
   
    template<typename U>
    _T& operator%=(const U &other){ return data[0]%=other; }
   
    template<typename U>
    _T& operator<<=(const U &other){ return data[0]<<=other; }
   
    template<typename U>
    _T& operator>>=(const U &other){ return data[0]>>=other; }
   
    template<typename U>
    _T& operator^=(const U &other){ return data[0]^=other; }
   
    template<typename U>
    _T& operator&=(const U &other){ return data[0]&=other; }
   
    template<typename U>
    _T& operator|=(const U &other){ return data[0]|=other; }
   
  private:
    _T *data;
  };
 
  index_functor<T,dim-1>
  operator[](long index)
  {
    if(getready())
      return index_functor<T,dim>(data,rl)[index];
    else
      throw(std::runtime_error("array access error: shape uninitialized"));
  }
 
  struct dummy{void nonnull(void){}};
  typedef void (dummy::*safe_bool)(void);
  operator safe_bool() {return data==0?0:&dummy::nonnull;}
 
  bool empty() const {return data==0;}
 
  size_type size() const { return length; }
 
  size_type max_size() const { return max_sz; }
 
  void clear()
  {
    if(data)
    {
      deallocate(data,max_sz);
      data=0;
      max_sz=0;
    }
  }
 
  void operator=(int zero){ clear(); }
 
  iterator begin() { return data; }
  const_iterator begin() const { return data; }
 
  iterator end() { return data+length; }
  const_iterator end() const { return data+length; }
 
private:
  pointer allocate(std::size_t n){ return static_cast<pointer>(Allocator::allocate(n)); }
  void deallocate(pointer p, std::size_t sz){ Allocator::deallocate(static_cast<void*>(p),sz); }
  pointer reallocate(pointer p, std::size_t _old_sz, std::size_t _new_sz){ return static_cast<pointer>(
      Allocator::reallocate(static_cast<void*>(p),_old_sz,_new_sz)
      ); }
  bool getready();

private:
  range_list rl[dim];
  bool range_changed;
  T* data;
  std::size_t length;
  std::size_t max_sz;
};

template<typename T, std::size_t dim, typename Allocator>
bool auto_array<T,dim,Allocator>::getready()
{
  if(range_changed)
  {
    std::size_t _new_length=1;
    for(long i=dim-1;i>=0;--i)
    {
      _new_length*=rl[i].width;
      rl[i].step = (i+1==dim?1:rl[i+1].step*rl[i+1].width);
    }
    if(_new_length>max_sz)
    {
      do{
        max_sz = (max_sz==0?128:(max_sz<<1));
      }while(_new_length>max_sz);
      if(data)
        data=reallocate(data,0,max_sz);
      else
        data=allocate(max_sz);
    }
    length=_new_length;
    range_changed=false;
  }
  return length>0;
}

#endif

// by rickone 2008/02/18

///////////////////////////////////////////////////////////////////////////////
// 最近在写游戏，就以地图类模版为例说明如何模拟多维数组吧！

    template <typename T_CELL_STYLE>
    class CMap
    {
    public:
        CMap(IN UINT row_num, IN UINT col_num, 
                  IN T_CELL_STYLE cell_style = static_cast<T_CELL_STYLE>(0));
 
        // 下标操作符重载
        typename vector<T_CELL_STYLE>::iterator operator[](IN UINT x);

    public:
        const UINT m_ROW_NUM;    // 地图网格行数
        const UINT m_COL_NUM;    // 地图网格列数
    private:
        vector<T_CELL_STYLE> _m_map_data;    // 存放地图数据
 
    }; /* class CMap */

// 我们知道下标操作符重载不能编写成如下形式：

    T_CELL_STYLE operator[][](IN UINT x, IN UINT y);

// 虽然不能直接实现一对下标操作符重载，但是我们可以间接模拟。

// 思路是这样的，先通过单下标操作返回一个具有下标操作能力的左值，对左值进行下标操作，两个下标操作表达式联立就实现了双下标操作。先看如下示例：

    // 地图尺寸
    #define _MAP_ROW   30
    #define _MAP_COL    36
    // 地图单元格样式
    typedef enum {
        _CELL_GROUND,
        _CELL_GRASS,
        _CELL_BRICK,
        _CELL_STEEL,
        _CELL_WATER
    } CELLSTYLE;
 
    CMap<CELLSTYLE> myMap(_MAP_ROW, _MAP_COL, _CELL_GROUND);
    // 获取地图第3行第5列单元格样式
    vector<T_CELL_STYLE>::iterator iter = myMap[3];
    CELLSTYLE aCell = iter[5];

// 我们将上面两个下标操作表达式联立，如下：
    CELLSTYLE aCell myMap[3][5];

// 这样就得到了双下标操作，看起来就像操作二维数组。好了，让我们来看一下如何重载。

    template <typename T_CELL_STYLE>
    inline typename vector<T_CELL_STYLE>::iterator
    CMap<T_CELL_STYLE>::operator[](IN UINT x)
    {
        if (m_ROW_NUM <= x)
        {
            overflow_error e("overflow - CMap<T_CELL_STYLE>::operator[]");
            throw(e);
        }
        return _m_map_data.begin() + x * m_COL_NUM;
    }

// 看到了吧，是不是很简单，中间过程借用了一个具有下标操作能力的类类型成员。

